home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / aminet / comm / misc / xqsrc1_7.lzh / library / queue.c < prev    next >
C/C++ Source or Header  |  1993-03-12  |  22KB  |  1,044 lines

  1. /*
  2.  *    Name:                queue.c
  3.  *
  4.  * Description:    Queue management functions for xferq.library
  5.  *
  6.  * Copyright:        1992-1993 by David Jones.
  7.  *
  8.  * Distribution:
  9.  *        This program is free software; you can redistribute it and/or modify
  10.  *        it under the terms of the GNU General Public License as published by
  11.  *        the Free Software Foundation; either version 2 of the License, or
  12.  *        (at your option) any later version.
  13.  *
  14.  *        This program is distributed in the hope that it will be useful,
  15.  *        but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.  *        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.  *        GNU General Public License for more details.
  18.  *
  19.  *        You should have received a copy of the GNU General Public License
  20.  *        along with this program; if not, write to:
  21.  *
  22.  *                The Free Software Foundation        David Jones
  23.  *                675 Mass Ave                            6730 Tooney Drive
  24.  *                Cambridge, MA                            Orleans, Ontario
  25.  *                02139                                        K1C 6R4
  26.  *                USA                                        Canada
  27.  *
  28.  *    Usenet:    gnu@prep.ai.mit.edu                    aa457@freenet.carleton.ca
  29.  *    Fidonet:                                                1:163/109.8
  30.  *
  31.  *        $Log: $
  32.  *
  33.  */
  34.  
  35. #include <exec/types.h>
  36. #include <exec/lists.h>
  37. #include <exec/nodes.h>
  38. #include <exec/semaphores.h>
  39. #include <utility/tagitem.h>
  40. #include <proto/exec.h>
  41. #include <proto/utility.h>
  42. #include <proto/dos.h>
  43. #include "xferq.h"
  44. #include "xferqint.h"
  45. #include "xferq_pragmas.h"
  46. #include "execlists.h"
  47.  
  48. /*
  49.  *        Variables
  50.  */
  51.  
  52. struct SignalSemaphore QueueLock;
  53. struct MinList QueueList;
  54. BOOL QueueScanned;
  55.  
  56. extern struct Library *UtilityBase;
  57.  
  58. struct EligibleWorkData {
  59.     struct ExtSessWalk esw;
  60.     struct WorkNode *wn;
  61.     BOOL incLocked;
  62.     short minPri, maxPri;
  63. };
  64.  
  65. struct PendingWorkData {
  66.     struct ExtSessWalk esw;
  67.     BOOL pending;
  68. };
  69.  
  70. struct FindWorkData {
  71.     struct ExtSessWalk esw;
  72.     struct WorkNode *wn;
  73.     char *name;
  74. };
  75.  
  76.  
  77. void InitQueue(void)
  78. /*
  79.  *    Does:    Initializes the queue manager data structures.
  80.  */
  81. {
  82.  
  83.     InitSemaphore(&QueueLock);
  84.     NEWLIST(&QueueList);
  85. #ifdef DEBUG
  86.     kprintf("QueueList = %08lx\n", &QueueList);
  87. #endif
  88.     QueueScanned = FALSE;
  89. }
  90.  
  91.  
  92. struct SiteNode *NewSiteQueue(struct NetAddress *site)
  93. /*
  94.  *    In:    site                Site to create queue for
  95.  *
  96.  * Does:    Allocates a new site queue and links it in to the global
  97.  *            structure.
  98.  */
  99. {
  100. struct SiteNode *sn;
  101.  
  102.     sn = AllocObject(sizeof(struct SiteNode), XQO_SITENODE);
  103.     if (!sn) {
  104.         return NULL;
  105.     }
  106.     sn->site = XfqCopyObject(site);
  107.     NEWLIST(&sn->workList);
  108.     sn->flags = 0;
  109.     sn->numSessions = 0;
  110.     sn->numLocks = 0;
  111.     ADDHEAD(&QueueList, sn);
  112.     return sn;
  113. }
  114.  
  115.  
  116. void DropSiteNode(struct SiteNode *sn)
  117. /*
  118.  *    In:    sn                    Pointer to site node to drop
  119.  *
  120.  *    Does:    Frees memory for all work nodes queued to site node.
  121.  */
  122. {
  123. struct WorkNode *wn, *twn;
  124.  
  125.     wn = FIRST(&sn->workList);
  126.     while (twn = NEXT(wn)) {
  127.         XfqDropObject(wn);
  128.         wn = twn;
  129.     }
  130. }
  131.  
  132.  
  133. void DropQueue(void)
  134. /*
  135.  *    Does:    Frees memory for all queue nodes.
  136.  */
  137. {
  138. struct SiteNode *sn, *tsn;
  139.  
  140.     sn = FIRST(&QueueList);
  141.     while (tsn = NEXT(sn)) {
  142.         DropSiteNode(sn);
  143.         FreeObject(sn);
  144.         sn = tsn;
  145.     }
  146.     InitQueue();
  147. }
  148.  
  149.  
  150. struct SiteNode *FindSiteQueue(struct NetAddress *addr)
  151. /*
  152.  *    In:    addr                Address to find site queue for
  153.  *
  154.  *    Does:    Finds the site queue corresponding to the supplied address.
  155.  *            If not found, this function tries to create
  156.  *            a new site queue.  The function returns a pointer to a site
  157.  *            queue, or NULL if error.
  158.  */
  159. {
  160. struct SiteNode *sn;
  161.  
  162.     if (!QueueScanned) {
  163.         ScanQueue();
  164.         if (!QueueScanned) {
  165.             return NULL;
  166.         }
  167.     }
  168.     sn = FIRST(&QueueList);
  169.     while (NEXT(sn)) {
  170.         if (!XfqCmpAddressTags(addr, sn->site,
  171.             XQ_Mandatory, XQADDR_ALLBUTUSER,
  172.             TAG_DONE)) {
  173.             if (sn->flags & XQSITE_UNREAD) {
  174.                 ReadQueue(sn);
  175.                 if (sn->flags & XQSITE_UNREAD) {
  176.                     return NULL;
  177.                 }
  178.             }
  179.             return sn;
  180.         }
  181.         sn = NEXT(sn);
  182.     }
  183.     sn = NewSiteQueue(addr);
  184.     return sn;
  185. }
  186.  
  187.  
  188. void ExtWalkSession(void *object, struct ExtSessWalk *esw)
  189. /*
  190.  *    In:    object            Object to walk
  191.  *            esw                Pointer to control data
  192.  *
  193.  * Does:    Performs actions similar to XfqWalkSessionCallBack(), but
  194.  *            which are more useful to the queue functions: for each site
  195.  *            node, data is filled in in the esw structure.
  196.  */
  197. {
  198. struct SiteNode *sq;
  199. struct SessNode *sn, *tsn;
  200.  
  201.     if (!object) {
  202.         /* Walk whole system */
  203.         sq = FIRST(&QueueList);
  204.         while (NEXT(sq)) {
  205.             esw->sn = sq;
  206.             esw->na = sq->site;
  207.             if (!(*esw->func)(esw)) {
  208.                 return;
  209.             }
  210.             sq = NEXT(sq);
  211.         }
  212.     }
  213.     else if (ObjectType(object) == XQO_ADDRESS) {
  214.         /* Access single address */
  215.         esw->na = object;
  216.         esw->sn = FindSiteQueue(object);
  217.         if (!esw->sn) {
  218.             return;
  219.         }
  220.         (*esw->func)(esw);
  221.     }
  222.     else if (ObjectType(object) == XQO_SESSION) {
  223.         /* Walk session */
  224.         sn = FIRST(&((struct Session *)object)->sessList);
  225.         while (tsn = NEXT(sn)) {
  226.             esw->na = sn->addr;
  227.             esw->sn = FindSiteQueue(sn->addr);
  228.             if (!esw->sn) {
  229.                 return;
  230.             }
  231.             if (!(*esw->func)(esw)) {
  232.                 return;
  233.             }
  234.             sn = tsn;
  235.         }
  236.     }
  237.     else {
  238.         Error(XQERROR_BADOBJECT);
  239.     }
  240. }
  241.  
  242.  
  243. BOOL EligibleWorkCB(struct EligibleWorkData *ewd)
  244. /*
  245.  *    In:    ewd                Pointer to working data
  246.  *
  247.  * Does:    Searches for eligible work and stores a pointer to it in
  248.  *            the hook data area.
  249.  */
  250. {
  251. struct WorkNode *wn;
  252.  
  253.     wn = FIRST(&ewd->esw.sn->workList);
  254. #ifdef DEBUG
  255.     kprintf("EW sn=%08lx pl=%08lx ph=%08lx\n", ewd->esw.sn,
  256.         ewd->minPri, ewd->maxPri);
  257. #endif
  258.     while (NEXT(wn)) {
  259. #ifdef DEBUG
  260.         kprintf("EW node W=%08lx SF=%02lx UF=%02lx ST=%ld, P=%ld\n",
  261.             wn, wn->sysFlags, wn->userFlags, wn->status, wn->node.ln_Pri);
  262. #endif
  263.         if (wn->status == XQ_UNSENT && !(wn->userFlags & XQ_SENDLATER) &&
  264.             (ewd->incLocked || !(wn->sysFlags & XQ_LOCKED)) &&
  265.             wn->node.ln_Pri >= ewd->minPri &&
  266.             wn->node.ln_Pri <= ewd->maxPri) {
  267.             ewd->wn = wn;
  268. #ifdef DEBUG
  269.             kprintf("EW found %08lx.\n", wn);
  270. #endif
  271.             return FALSE;
  272.         }
  273.         wn = NEXT(wn);
  274.     }
  275. #ifdef DEBUG
  276.     kprintf("EW found nothing.\n");
  277. #endif
  278.     return TRUE;
  279. }
  280.  
  281.  
  282. struct WorkNode *EligibleWork(void *object, struct TagItem *tags)
  283. /*
  284.  *    In:    object            Object to find eligible work for.
  285.  *            tags                Taglist controlling scope of search.
  286.  *
  287.  * Does:    Finds work that can be sent.  If incLocked is TRUE, then locked
  288.  *            work is also considered, even though it cannot be sent.
  289.  */
  290. {
  291. struct EligibleWorkData ewd;
  292.  
  293.     ewd.incLocked = GetTagData(XQ_IncLocked, FALSE, tags);
  294.     ewd.minPri = GetTagData(XQ_MinPri, XQ_MINPRI, tags);
  295.     ewd.maxPri = GetTagData(XQ_MaxPri, XQ_MAXPRI, tags);
  296. #ifdef DEBUG
  297.     kprintf("Entered EligibleWork(%08lx,%ld)\n", object, ewd.incLocked);
  298. #endif
  299.     ewd.wn = NULL;
  300.     ewd.esw.func = EligibleWorkCB;
  301.     ExtWalkSession(object, &ewd);
  302. #ifdef DEBUG
  303.     kprintf("Left EligibleWork\n");
  304. #endif
  305.     return ewd.wn;
  306. }
  307.  
  308.  
  309. struct WorkNode *EligibleWorkTags(void *object, Tag tag, ...)
  310. {
  311.  
  312.     return EligibleWork(object, (struct TagItem *)&tag);
  313. }
  314.  
  315.  
  316. BOOL AnyDirty(void)
  317. /*
  318.  *    Does:    Returns TRUE if any of the queues are dirty, FALSE otherwise.
  319.  */
  320. {
  321. struct SiteNode *sn;
  322.  
  323.     sn = FIRST(&QueueList);
  324.     while (NEXT(sn)) {
  325.         if (sn->flags & XQSITE_DIRTY) {
  326.             return TRUE;
  327.         }
  328.         sn = NEXT(sn);
  329.     }
  330.     return FALSE;
  331. }
  332.  
  333.  
  334. struct WorkNode *LockWork(struct WorkNode *wn)
  335. /*
  336.  *    In:    wn                    Pointer to node to lock.
  337.  *
  338.  *    Does:    If node is locked, then set error code and return NULL.
  339.  *            Otherwise assert lock bit and return pointer to node.
  340.  */
  341. {
  342.  
  343.     if (wn->sysFlags & XQ_LOCKED) {
  344.         Error(XQERROR_LOCKED);
  345.         return NULL;
  346.     }
  347.     else {
  348.         wn->sysFlags |= XQ_LOCKED;
  349.         return wn;
  350.     }
  351. }
  352.  
  353.  
  354. void __saveds __asm LIBUnlockWork(register __a0 struct WorkNode *wn)
  355. /*
  356.  *    In:    wn            A0        Pointer to node to unlock
  357.  *
  358.  *    Does:    Clears the lock bit in the work node.
  359.  */
  360. {
  361.  
  362.     ObtainSemaphore(&QueueLock);
  363.     wn->sysFlags &= ~XQ_LOCKED;
  364.     ReleaseSemaphore(&QueueLock);
  365. }
  366.  
  367.  
  368. BOOL PendingWorkCB(struct PendingWorkData *pwd)
  369. /*
  370.  *    In:    pwd                Pointer to working data
  371.  *
  372.  * Does:    Searches for pending work and stores a pointer to it in
  373.  *            the hook data area.
  374.  */
  375. {
  376.  
  377.     if (pwd->esw.sn->numLocks) {
  378.         pwd->pending = TRUE;
  379.         return FALSE;
  380.     }
  381.     else {
  382.         return TRUE;
  383.     }
  384. }
  385.  
  386.  
  387. ULONG __saveds __asm LIBAnyWork(register __a0 void *object,
  388.     register __a1 struct TagItem *tags)
  389. /*
  390.  *    In:    object    A0        Pointer to object to check work for
  391.  *            tags        A1        Taglist giving priority window
  392.  *
  393.  *    Does:    Checks to see if any work is queued to the object in question.
  394.  *            The function returns one of the following:
  395.  *
  396.  *            NO_WORK            No work is queued up
  397.  *            LOCKED_WORK        Work is queued, but other users have locked it
  398.  *            UNLOCKED_WORK    Work is queued and unlocked
  399.  *
  400.  *            The object passed in may be a session or an address.  If it's
  401.  *            a session, the function checks every address in the session
  402.  *            for work.
  403.  */
  404. {
  405. ULONG result;
  406. struct PendingWorkData pwd;
  407.  
  408.     ObtainSemaphore(&QueueLock);
  409. #ifdef DEBUG
  410.     kprintf("In AnyWork - ObjectType %ld\n", ObjectType(object));
  411. #endif
  412.     if (EligibleWorkTags(object,
  413.         XQ_IncLocked, FALSE,
  414.         TAG_MORE, tags)) {
  415.         result = UNLOCKED_WORK;
  416.     }
  417.     else if (EligibleWorkTags(object,
  418.         XQ_IncLocked, TRUE,
  419.         TAG_MORE, tags)) {
  420.         result = LOCKED_WORK;
  421.     }
  422.     else {
  423.         pwd.pending = FALSE;
  424.         pwd.esw.func = PendingWorkCB;
  425.         ExtWalkSession(object, &pwd);
  426.         if (pwd.pending) {
  427.             result = PENDING_WORK;
  428.         }
  429.         else {
  430.             result = NO_WORK;
  431.         }
  432.     }
  433.     ReleaseSemaphore(&QueueLock);
  434. #ifdef DEBUG
  435.     kprintf("Left AnyWork result %ld.\n", result);
  436. #endif
  437.     SetErrorTags(tags);
  438.     return result;
  439. }
  440.  
  441.  
  442. BOOL FindWorkCB(struct FindWorkData *fwd)
  443. /*
  444.  *    In:    fwd                Pointer to state info
  445.  *
  446.  * Does:    Searches for the name in the given queue and aborts the walk
  447.  *            if found.
  448.  */
  449. {
  450.  
  451.     fwd->wn = (struct WorkNode *)FindNameNoCase(&fwd->esw.sn->workList,
  452.         fwd->name);
  453.     /*
  454.      *    Abort walk only if something found.
  455.      */
  456.     if (fwd->wn) {
  457.         return FALSE;
  458.     }
  459.     else {
  460.         return TRUE;
  461.     }
  462. }
  463.  
  464.  
  465. struct WorkNode *__saveds __asm LIBFindWork(
  466.     register __a0 struct TagItem *tags)
  467. /*
  468.  *    In:    tags        A0        Pointer to tag list
  469.  *
  470.  *    Does:    Searches the queue for work satisfying the conditions set by
  471.  *            the tag list.  If a work node is found, its lock bit is set
  472.  *            and its address is returned.  Otherwise, the function returns
  473.  *            NULL.  Valid tags are:
  474.  *
  475.  *            XQ_Name        Fully-pathed filename of file (mandatory)
  476.  *            XQ_Site        Site that file was sent to (optional)
  477.  *
  478.  *            If XQ_Site is not given, all sites in the queue are searched.
  479.  *            If the file is being sent to more than one site, you'll never
  480.  *            find out.
  481.  *
  482.  *            If XQ_Site is given, it may be either an address or a session.
  483.  *            If a session, all sites on the session are searched.
  484.  */
  485. {
  486. struct FindWorkData fwd;
  487. void *site;
  488. char *name;
  489. struct WorkNode *wn;
  490.  
  491.     name = (char *)GetTagData(XQ_Name, NULL, tags);
  492.     site = (void *)GetTagData(XQ_Site, NULL, tags);
  493.     
  494.     /*
  495.      *    The name is stored fully pathed so it must be searched fully
  496.      * pathed.
  497.      */
  498.     fwd.name = FullPath(name);
  499.     if (!fwd.name) {
  500.         SetErrorTags(tags);
  501.         return NULL;
  502.     }
  503.  
  504.     fwd.wn = NULL;
  505.     fwd.esw.func = FindWorkCB;
  506.     
  507.     ObtainSemaphore(&QueueLock);
  508.     ExtWalkSession(site, &fwd);
  509.     if (fwd.wn) {
  510.         wn = LockWork(fwd.wn);
  511.     }
  512.     else {
  513.         Error(XQERROR_NOEXIST);
  514.         wn = NULL;
  515.     }
  516.     ReleaseSemaphore(&QueueLock);
  517.     XfqDropObject(fwd.name);
  518.     
  519.     SetErrorTags(tags);
  520.     return wn;
  521. }
  522.  
  523.  
  524. struct WorkNode *__saveds __asm LIBGetWork(register __a0 void *object,
  525.     register __a1 struct TagItem *tags)
  526. /*
  527.  *    In:    object    A0        Pointer to address or session
  528.  *            tags        A1        Taglist giving priority window
  529.  *
  530.  *    Does:    Finds the next item of work that can be sent, locks it and
  531.  *            returns it.  If object is an address, then only that queue
  532.  *            is searched.  If object is a session, then all addresses in
  533.  *            the session are searched.
  534.  *
  535.  *            Criteria for transmission include:
  536.  *            -    Work must not be locked.
  537.  *            -    Work must be UNSENT.
  538.  *            -    Work must be within priority bounds of taglist
  539.  *            -    Work must be higher priority than other eligible work
  540.  *            -    Work must not be SENDLATER
  541.  */
  542. {
  543. struct WorkNode *wn;
  544.  
  545.     ObtainSemaphore(&QueueLock);
  546. #ifdef DEBUG
  547.     kprintf("In GetWork - ObjectType %ld\n", ObjectType(object));
  548. #endif
  549.     wn = EligibleWorkTags(object,
  550.         XQ_IncLocked, FALSE,
  551.         TAG_MORE, tags);
  552.     if (wn) {
  553.         LockWork(wn);
  554.     }
  555.     else {
  556.         Error(XQERROR_NOEXIST);
  557.     }
  558.     ReleaseSemaphore(&QueueLock);
  559. #ifdef DEBUG
  560.     kprintf("Left GetWork with %08lx.\n", wn);
  561. #endif
  562.     SetErrorTags(tags);
  563.     return wn;
  564. }
  565.  
  566.  
  567. ULONG __saveds __asm LIBRemoveWork(register __a0 struct WorkNode *wn)
  568. /*
  569.  *    In:    wn            A0        Pointer to node to remove
  570.  *
  571.  *    Does:    Removes the node from its queue so that NO queue management
  572.  *            function can see it.  XfqAddWork() can be used to re-insert
  573.  *            the node.
  574.  *
  575.  *            If the file has been sent and deletion or truncation has been
  576.  *            specified, then kiss the file good-bye.
  577.  */
  578. {
  579. ULONG result;
  580.  
  581.     ObtainSemaphore(&QueueLock);
  582.     if (wn->status == XQ_SENT) {
  583.         if (wn->userFlags & XQ_DELETE) {
  584.             DeleteFile(wn->node.ln_Name);
  585.             if (IoErr()) {
  586.                 Error(XQERROR_DOS);
  587.                 result = XQRM_ERROR;
  588.             }
  589.             else {
  590.                 result = XQRM_DELETED;
  591.             }
  592.         }
  593.         else if (wn->userFlags & XQ_TRUNCATE) {
  594.             result = TruncateFile(wn->node.ln_Name);
  595.         }
  596.         else {
  597.             result = XQRM_SENT;
  598.         }
  599.     }
  600.     else {
  601.         result = XQRM_UNSENT;
  602.     }
  603.     REMOVE(wn);
  604.     wn->site->flags |= XQSITE_DIRTY;
  605.     wn->sysFlags &= ~XQ_INQUEUE;
  606.     ReleaseSemaphore(&QueueLock);
  607.     return result;
  608. }
  609.  
  610.  
  611. LONG __saveds __asm LIBMaxSitePri(register __a0 void *object)
  612. /*
  613.  *    In:    object    A0        Pointer to address or session
  614.  *
  615.  *    Does:    Determines the maximum priority of any work described by
  616.  *            the supplied object.  Object may be either a site or a session.
  617.  */
  618. {
  619. struct WorkNode *wn;
  620. LONG result;
  621.  
  622.     ObtainSemaphore(&QueueLock);
  623.     wn = EligibleWorkTags(object,
  624.         XQ_IncLocked, FALSE,
  625.         TAG_DONE);
  626.     if (wn) {
  627.         result = wn->node.ln_Pri;
  628.     }
  629.     else {
  630.         result = XQ_NOPRI;
  631.     }
  632.     ReleaseSemaphore(&QueueLock);
  633.     return result;
  634. }
  635.  
  636.  
  637. void __saveds __asm LIBWalkQueueCallBack(
  638.     register __a0 struct NetAddress *addr,
  639.     register __a1 struct TagItem *tags,
  640.     register __a4 void *globals)
  641. /*
  642.  *    In:    addr        A0        Pointer to address to walk through
  643.  *            tags        A1        Pointer to tag list
  644.  *            globals    A4        Pointer to caller's global area
  645.  *
  646.  *    Does:    Calls a user-supplied hook function for each entry in the
  647.  *            queue.  The function must return TRUE to continue the walk,
  648.  *            FALSE otherwise.  Valid tags are:
  649.  *
  650.  *            XQ_Reverse        Set to TRUE to walk from low-to-high priority
  651.  *            XQ_CallBack        Data is pointer to hook structure.
  652.  *
  653.  *    Note:    Access to all queues in the system is disabled while this
  654.  *            function executes.  Be quick!
  655.  */
  656. {
  657. struct SiteNode *sn;
  658. struct WorkNode *wn, *twn;
  659. struct Hook *hook;
  660. BOOL reverse, incLocked;
  661.  
  662.     hook = (struct Hook *)GetTagData(XQ_CallBack, NULL, tags);
  663.     if (!hook) {
  664.         Error(XQERROR_NOTAG);
  665.         SetErrorTags(tags);
  666.         return;
  667.     }
  668.     reverse = (BOOL)GetTagData(XQ_Reverse, FALSE, tags);
  669.     incLocked = (BOOL)GetTagData(XQ_IncLocked, FALSE, tags);
  670.     ObtainSemaphore(&QueueLock);
  671.     sn = FindSiteQueue(addr);
  672.     if (!sn) {
  673.         Error(XQERROR_NOEXIST);
  674.         SetErrorTags(tags);
  675.         return;
  676.     }
  677.     if (reverse) {
  678.         wn = LAST(&sn->workList);
  679.         while (twn = PREV(wn)) {
  680.             if (incLocked || !(wn->sysFlags & XQ_LOCKED)) {
  681.                 if (!CallHookRes(hook, wn, tags, globals)) {
  682.                     break;
  683.                 }
  684.             }
  685.             wn = twn;
  686.         }
  687.     }
  688.     else {
  689.         wn = FIRST(&sn->workList);
  690.         while (twn = NEXT(wn)) {
  691.             if (incLocked || !(wn->sysFlags & XQ_LOCKED)) {
  692.                 if (!CallHookRes(hook, wn, tags, globals)) {
  693.                     break;
  694.                 }
  695.             }
  696.             wn = twn;
  697.         }
  698.     }
  699.     SetErrorTags(tags);
  700.     ReleaseSemaphore(&QueueLock);
  701. }
  702.  
  703.  
  704. ULONG __saveds __asm LIBAddWork(register __a0 struct NetAddress *addr,
  705.     register __a1 struct WorkNode *wn)
  706. /*
  707.  *    In:    addr        A0        Address to add work to
  708.  *            wn            A1        Work node to add
  709.  *
  710.  * Does:    Enqueues the work onto the given queue.
  711.  *
  712.  */
  713. {
  714. struct SiteNode *sn;
  715. struct FindWorkData fwd;
  716.  
  717.     ObtainSemaphore(&QueueLock);
  718.     /*
  719.      *    If IMMEDIATE and no session is up, then don't add work.
  720.      */
  721.     if ((wn->userFlags & XQ_IMMEDIATE) && !XfqSessionUp(addr)) {
  722.         Error(XQERROR_NOSESSION);
  723.         ReleaseSemaphore(&QueueLock);
  724.         return FALSE;
  725.     }
  726.     /*
  727.      * If SENDLATER and no session is up, then clear the SENDLATER bit.
  728.      */
  729.     if ((wn->userFlags & XQ_SENDLATER) && !XfqSessionUp(addr)) {
  730.         wn->userFlags &= ~XQ_SENDLATER;
  731.     }
  732.     /*
  733.      * Check to see if work is already in the queue.  This is the
  734.      * only reliable way to perform this check.
  735.      */
  736.     fwd.name = wn->node.ln_Name;
  737.     fwd.wn = NULL;
  738.     fwd.esw.func = FindWorkCB;
  739.     ExtWalkSession(addr, &fwd);
  740.     if (fwd.wn) {
  741.         Error(XQERROR_QUEUED);
  742.         ReleaseSemaphore(&QueueLock);
  743.         return FALSE;
  744.     }
  745.     /*
  746.      *    Find place to add work.
  747.      */
  748.     sn = FindSiteQueue(addr);
  749.     if (!sn) {
  750.         ReleaseSemaphore(&QueueLock);
  751.         return FALSE;
  752.     }
  753.     ENQUEUE(&sn->workList, wn);
  754.     /*
  755.      *    Fix up links to address, siteNode and dirty queue.
  756.      */
  757.     wn->site = sn;
  758.     if (wn->addr) {
  759.         XfqDropObject(wn->addr);
  760.     }
  761.     XfqCopyObject(sn->site);
  762.     wn->addr = sn->site;
  763.     wn->sysFlags |= XQ_INQUEUE | XQ_LOCKED;
  764.     sn->flags |= XQSITE_DIRTY;
  765.     ReleaseSemaphore(&QueueLock);
  766.     return TRUE;
  767. }
  768.  
  769.  
  770. BOOL PreExamWork(struct WorkNode *wn)
  771. /*
  772.  *    In:    wn                    Work node to examine.
  773.  *
  774.  * Does:    Locks the queue for data integrity.
  775.  */
  776. {
  777.  
  778.     ObtainSemaphore(&QueueLock);
  779.     return TRUE;
  780. }
  781.  
  782. BOOL PostExamWork(struct WorkNode *wn)
  783. /*
  784.  *    In:    wn                    Work node to examine.
  785.  *
  786.  * Does:    Releases lock after examining node.
  787.  */
  788. {
  789.  
  790.     ReleaseSemaphore(&QueueLock);
  791.     return TRUE;
  792. }
  793.  
  794.  
  795. BOOL PreModifyWork(struct WorkNode *wn)
  796. /*
  797.  *    In:    wn                    Work node to modify
  798.  *
  799.  *    Does:    This function is called before a work node is modified.  It
  800.  *            removes the node from the queue for mutex purposes.
  801.  */
  802. {
  803.  
  804.     ObtainSemaphore(&QueueLock);
  805.     /*
  806.      *    If the node is in the queue, then remove it.
  807.      */
  808.     if (wn->sysFlags & XQ_INQUEUE) {
  809.         REMOVE(wn);
  810.     }
  811.     return TRUE;
  812. }
  813.  
  814.  
  815. BOOL PostModifyWork(struct WorkNode *wn)
  816. /*
  817.  *    In:    wn                    Pointer to modified work
  818.  *
  819.  * Does:    Inserts the work node into the proper spot on the queue.
  820.  */
  821. {
  822. char *name;
  823.  
  824.     /*
  825.      *    Compute full path of name
  826.      */
  827.     name = wn->node.ln_Name;
  828.     wn->node.ln_Name = FullPath(name);
  829.     XfqDropObject(name);
  830.     if (!wn->node.ln_Name) {
  831.         ReleaseSemaphore(&QueueLock);
  832.         return FALSE;
  833.     }
  834.     /*
  835.      *    If the node was in the queue at the time the modification started,
  836.      * then re-insert into the queue.
  837.      */
  838.     if (wn->sysFlags & XQ_INQUEUE) {
  839.         XfqAddWork(wn->addr, wn);
  840.     }
  841.     ReleaseSemaphore(&QueueLock);
  842.     return TRUE;
  843. }
  844.  
  845.  
  846. struct Session *__saveds __asm LIBGetSiteList(void)
  847. /*
  848.  *    Does:    Creates a private session whose addresses are those that have
  849.  *            work queued to them.
  850.  */
  851. {
  852. struct Session *session;
  853. struct SessNode *sn;
  854. struct SiteNode *sq, *tsq;
  855.  
  856.     session = XfqCreateObjectTags(XQO_SESSION,
  857.         XQ_SessFlags, XQSESS_PRIVATE,
  858.         TAG_DONE);
  859.     if (!session) {
  860.         return NULL;
  861.     }
  862.     ObtainSemaphore(&QueueLock);
  863.     if (!QueueScanned) {
  864.         ScanQueue();
  865.         if (!QueueScanned) {
  866.             ReleaseSemaphore(&QueueLock);
  867.             return NULL;
  868.         }
  869.     }
  870.     sq = FIRST(&QueueList);
  871.     while (tsq = NEXT(sq)) {
  872.         if (sq->flags & XQSITE_UNREAD) {
  873.             ReadQueue(sq);
  874.             if (sq->flags & XQSITE_UNREAD) {
  875.                 XfqDropObject(session);
  876.                 ReleaseSemaphore(&QueueLock);
  877.                 return NULL;
  878.             }
  879.         }
  880.         if (NEXT(FIRST(&sq->workList))) {
  881.             sn = AllocObject(sizeof(struct SessNode), XQO_SESSNODE);
  882.             if (sn) {
  883.                 sn->addr = sq->site;
  884.                 XfqCopyObject(sq->site);
  885.                 ADDHEAD(&session->sessList, sn);
  886.             }
  887.             else {
  888.                 XfqDropObject(session);
  889.                 ReleaseSemaphore(&QueueLock);
  890.                 return NULL;
  891.             }
  892.         }
  893.         sq = tsq;
  894.     }
  895.     ReleaseSemaphore(&QueueLock);
  896.     return session;
  897. }
  898.  
  899.  
  900. ULONG __saveds __asm LIBAddWorkQuick(register __a0 char *site,
  901.     register __a1 char *name, register __a2 char *asname,
  902.     register __d0 LONG pri, register __d1 LONG flags)
  903. /*
  904.  *    In:    site        A0        Address STRING to add work to
  905.  *            name        A1        Name of file to add
  906.  *            asname    A2        Name to send file as
  907.  *            pri        D0        Priority to send file at
  908.  *            flags        D1        Flags controlling file handling
  909.  *
  910.  *    Does:    This is a quick-and-dirty addition function.  It parses the
  911.  *            address string, creates a work node and adds it to the queue.
  912.  *            Function returns TRUE if it worked.
  913.  */
  914. {
  915. struct NetAddress *addr;
  916. struct WorkNode *wn;
  917. ULONG *errp;
  918.  
  919.     addr = XfqGetAddressTags(site, NULL,
  920.         XQ_Mandatory, XQADDR_2D,
  921.         XQ_Optional, XQADDR_ANYTHING,
  922.         TAG_DONE);
  923. #ifdef DEBUG
  924.     kprintf("AddWorkQuick address = %08lx\n", addr);
  925. #endif
  926.     if (!addr) {
  927.         return FALSE;
  928.     }
  929.     
  930.     /* Create the work node */
  931.     wn = XfqCreateObjectTags(XQO_WORKNODE,
  932.         XQ_Name, name,
  933.         XQ_AsName, asname,
  934.         XQ_Pri, pri,
  935.         XQ_Flags, flags,
  936.         TAG_DONE);
  937.     if (!wn) {
  938.         XfqDropObject(addr);
  939.         return FALSE;
  940.     }
  941.     
  942.     /* Add the work node */
  943.     XfqAddWork(addr, wn);
  944.     if (!(wn->sysFlags & XQ_INQUEUE)) {
  945.         XfqDropObject(addr);
  946.         return FALSE;
  947.     }
  948.     XfqUnlockWork(wn);
  949.     
  950.     /* The address is no longer needed */
  951.     XfqDropObject(addr);
  952.     return TRUE;
  953. }
  954.  
  955.  
  956. void __saveds __asm LIBFlushQueue(register __a0 void *object)
  957. /*
  958.  *    In:    object    A0        Pointer to object to flush
  959.  *
  960.  *    Does:    Writes one or more site queues out to disk.  If object is an
  961.  *            address, then only that site queue will be flushed.  If object
  962.  *            is a session, then all addresses in the session will be flushed.
  963.  *            If object is NULL, then all queues are flushed.
  964.  */
  965. {
  966. struct ExtSessWalk esw;
  967.  
  968.     ObtainSemaphore(&QueueLock);
  969.     esw.func = WriteQueue;
  970.     ExtWalkSession(object, &esw);
  971.     ReleaseSemaphore(&QueueLock);
  972. }
  973.  
  974.  
  975. void SessionDownQueue(struct SiteNode *sn)
  976. /*
  977.  *    In:    sn                    Site node to scan for dead work
  978.  *
  979.  * Does:    All sessions just went down, so remove all IMMEDIATE nodes and
  980.  *            clear the SENDLATER bits.
  981.  */
  982. {
  983. struct WorkNode *wn, *twn;
  984.  
  985.     wn = FIRST(&sn->workList);
  986.     while (twn = NEXT(wn)) {
  987.         wn->userFlags &= ~XQ_SENDLATER;
  988.         if (wn->userFlags & XQ_IMMEDIATE) {
  989.                         wn->status=XQ_SENT;
  990.             XfqRemoveWork(wn);
  991.             XfqDropObject(wn);
  992.         }
  993.         wn = twn;
  994.     }
  995. }
  996.  
  997.  
  998. ULONG __saveds __asm LIBHoldMailer(register __a0 struct NetAddress *addr)
  999. /*
  1000.  *    In:    addr        A0        Address to lock mailer for.
  1001.  *
  1002.  * Does:    Puts a mailer on hold by setting a flag that affects the result of
  1003.  *            XfqAnyWork().  This function is used to delay a mailer session
  1004.  *            while work is being created.
  1005.  */
  1006. {
  1007. struct SiteNode *sn;
  1008.  
  1009.     ObtainSemaphore(&QueueLock);
  1010.     sn = FindSiteQueue(addr);
  1011.     if (sn) {
  1012.         sn->numLocks++;
  1013.     }
  1014.     ReleaseSemaphore(&QueueLock);
  1015.     return (sn) ? (ULONG)TRUE : (ULONG)FALSE;
  1016. }
  1017.  
  1018.  
  1019. ULONG __saveds __asm LIBReleaseMailer(register __a0 struct NetAddress *addr)
  1020. /*
  1021.  *    In:    addr        A0        Address to release mailer for.
  1022.  *
  1023.  * Does:    Releases a hold requested by XfqHoldMailer().
  1024.  */
  1025. {
  1026. struct SiteNode *sn;
  1027. ULONG result;
  1028.  
  1029.     ObtainSemaphore(&QueueLock);
  1030.     sn = FindSiteQueue(addr);
  1031.     if (sn && sn->numLocks > 0) {
  1032.         sn->numLocks--;
  1033.         result = TRUE;
  1034.     }
  1035.     else {
  1036.         result = FALSE;
  1037.     }
  1038.     ReleaseSemaphore(&QueueLock);
  1039.     return result;
  1040. }
  1041.  
  1042.  
  1043.  
  1044.